import {Layout} from '../../src/Layout';
export default Layout;

import docs from 'docs:react-aria-components';
import vanillaDocs from 'docs:vanilla-starter/Modal';
import '../../tailwind/tailwind.css';
import Anatomy from '@react-aria/overlays/docs/modal-anatomy.svg';
import {InlineAlert, Heading, Content} from '@react-spectrum/s2'

export const tags = ['dialog', 'popup', 'overlay'];
export const relatedPages = [{'title': 'useModalOverlay', 'url': 'Modal/useModalOverlay.html'}];
export const description = 'An overlay element which blocks interaction with elements outside it.';

# Modal

<PageDescription>{docs.exports.Modal.description}</PageDescription>

<ExampleSwitcher>
  ```tsx render docs={vanillaDocs.exports.Modal} links={vanillaDocs.links} props={['isDismissable', 'isKeyboardDismissDisabled']}  type="vanilla" files={["starters/docs/src/Modal.tsx", "starters/docs/src/Modal.css"]}
  "use client";
  import {DialogTrigger, Heading} from 'react-aria-components';
  import {Modal} from 'vanilla-starter/Modal';
  import {Dialog} from 'vanilla-starter/Dialog';
  import {Form} from 'vanilla-starter/Form';
  import {TextField} from 'vanilla-starter/TextField';
  import {Button} from 'vanilla-starter/Button';

  function Example(props) {
    return (
      <DialogTrigger>
        <Button>Sign up…</Button>
        <Modal {...props}/* PROPS */>
          <Dialog>
            <Heading slot="title">Subscribe to our newsletter</Heading>
            <p>Enter your information to subscribe to our newsletter and receive updates about new features and announcements.</p>
            <Form>
              <TextField autoFocus label="Name" placeholder="Enter your full name" />
              <TextField label="Email" placeholder="Enter your email" />
              <div style={{display: 'flex', gap: 8, alignSelf: 'end'}}>
                <Button slot="close" variant="secondary">Cancel</Button>
                <Button slot="close">Subscribe</Button>
              </div>
            </Form>
          </Dialog>
        </Modal>
      </DialogTrigger>
    );
  }
  ```

  ```tsx render docs={vanillaDocs.exports.Modal} links={vanillaDocs.links} props={['isDismissable', 'isKeyboardDismissDisabled']} type="tailwind" files={["starters/tailwind/src/Modal.tsx"]}
  "use client";
  import {DialogTrigger, Heading} from 'react-aria-components';
  import {Modal} from 'tailwind-starter/Modal';
  import {Dialog} from 'tailwind-starter/Dialog';
  import {Form} from 'tailwind-starter/Form';
  import {TextField} from 'tailwind-starter/TextField';
  import {Button} from 'tailwind-starter/Button';

  function Example(props) {
    return (
      <DialogTrigger>
        <Button>Sign up…</Button>
        <Modal {...props}/* PROPS */>
          <Dialog>
            <Heading slot="title" className="text-xl mt-0">Subscribe to our newsletter</Heading>
            <p className="text-sm">Enter your information to subscribe to our newsletter and receive updates about new features and announcements.</p>
            <Form>
              <TextField autoFocus label="Name" placeholder="Enter your full name" />
              <TextField label="Email" placeholder="Enter your email" />
              <div className="flex gap-2 self-end">
                <Button slot="close" variant="secondary">Cancel</Button>
                <Button slot="close">Subscribe</Button>
              </div>
            </Form>
          </Dialog>
        </Modal>
      </DialogTrigger>
    );
  }
  ```
</ExampleSwitcher>

## Sheet

Overlays such as trays, drawers, and sheets can be built using a `Modal` with custom entry and exit animations.

```tsx render type="vanilla" files={["starters/docs/src/Sheet.tsx", "starters/docs/src/Sheet.css"]}
"use client";
import {DialogTrigger, Heading} from 'react-aria-components';
import {Sheet} from 'vanilla-starter/Sheet';
import {Button} from 'vanilla-starter/Button';

<DialogTrigger>
  <Button>Open sheet</Button>
  <Sheet>
    <Heading slot="title">Notice</Heading>
    <p>This is a modal with a custom modal overlay.</p>
    <Button slot="close">Close</Button>
  </Sheet>
</DialogTrigger>
```

## Controlled

Use the `isOpen` prop to show a modal programmatically or mount in a different part of the JSX tree (e.g. outside a menu).

```tsx render
"use client";
import {useState} from 'react';
import {Heading} from 'react-aria-components';
import {MenuTrigger, Menu, MenuItem} from 'vanilla-starter/Menu';
import {Modal} from 'vanilla-starter/Modal';
import {Dialog} from 'vanilla-starter/Dialog';
import {Button} from 'vanilla-starter/Button';

function Example() {
  let [isOpen, setOpen] = useState(false);

  return (
    <>
      <MenuTrigger>
        <Button>Menu</Button>
        <Menu>
          <MenuItem onAction={() => setOpen(true)}>Open dialog…</MenuItem>
        </Menu>
      </MenuTrigger>
      <Modal isDismissable isOpen={isOpen} onOpenChange={setOpen}>
        <Dialog>
          <Heading slot="title">Notice</Heading>
          <p>Click outside or press Escape to close this dialog.</p>
        </Dialog>
      </Modal>
    </>
  );
}
```

## Custom trigger

`DialogTrigger` works with any pressable React Aria component (e.g. [Button](Button), [Link](Link), etc.). Use the `<Pressable>` component or [usePress](usePress) hook to wrap a custom trigger element such as a third party component or DOM element.

```tsx render
"use client"
import {Pressable, DialogTrigger, Heading} from 'react-aria-components';
import {Modal} from 'vanilla-starter/Modal';
import {Dialog} from 'vanilla-starter/Dialog';
import {Button} from 'vanilla-starter/Button';

<DialogTrigger>
  {/*- begin highlight -*/}
  <Pressable>
    <span role="button">Custom trigger</span>
  </Pressable>
  {/*- end highlight -*/}
  <Modal>
    <Dialog>
      <Heading slot="title">Dialog</Heading>
      <p>This dialog was triggered by a custom button.</p>
      <Button slot="close">Close</Button>
    </Dialog>
  </Modal>
</DialogTrigger>
```

<InlineAlert variant="notice">
  <Heading>Accessibility</Heading>
  <Content>Any `<Pressable>` child must have an [interactive ARIA role](https://www.w3.org/TR/wai-aria-1.2/#widget_roles) or use an appropriate semantic HTML element so that screen readers can announce the trigger. Trigger components must forward their `ref` and spread all props to a DOM element.</Content>
</InlineAlert>

```tsx
const CustomTrigger = React.forwardRef((props, ref) => (
  <button {...props} ref={ref} />
));
```

## Examples

<ExampleList tag="modal" pages={props.pages} />

## API

<Anatomy />

```tsx links={{DialogTrigger: '#dialogtrigger', Button: 'Button', ModalOverlay: '#modaloverlay', Modal: '#modal', Dialog: '#dialog'}}
<DialogTrigger>
  <Button />
  <ModalOverlay>
    <Modal>
      <Dialog />
    </Modal>
  </ModalOverlay>
</DialogTrigger>
```

### DialogTrigger

<PropTable component={docs.exports.DialogTrigger} links={docs.links} showDescription />

### Modal

<PropTable component={docs.exports.Modal} links={docs.links} showDescription />

### ModalOverlay

<PropTable
  component={docs.exports.ModalOverlay}
  links={docs.links}
  showDescription
  cssVariables={{
    '--visual-viewport-height': 'The height of the [Visual Viewport](https://developer.mozilla.org/en-US/docs/Web/API/Visual_Viewport_API), i.e. space above the software keyboard.',
    '--page-height': 'The height of the `<body>` element. Useful for sizing the modal backdrop.'
  }} />

### Dialog

<PropTable component={docs.exports.Dialog} links={docs.links} showDescription />
